#include <CLFX_LIB/CLFXMD5.h>


	typedef struct
	{
		unsigned int	rgState[4];		
		unsigned int	rgCount[4];		
		unsigned char	rgBuffer[64];	

	} CLFXMD5Context;

	//==========================================================================================================================
	
	static const unsigned int S11 = 7;
	static const unsigned int S12 = 12;
	static const unsigned int S13 = 17;
	static const unsigned int S14 = 22;
	static const unsigned int S21 = 5;
	static const unsigned int S22 = 9;
	static const unsigned int S23 = 14;
	static const unsigned int S24 = 20;
	static const unsigned int S31 = 4;
	static const unsigned int S32 = 11;
	static const unsigned int S33 = 16;
	static const unsigned int S34 = 23;
	static const unsigned int S41 = 6;
	static const unsigned int S42 = 10;
	static const unsigned int S43 = 15;
	static const unsigned int S44 = 21;

	static const unsigned char PADDING[64] =
	{
		0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	};

	
	inline unsigned int CLFXMD5_F(unsigned int x, unsigned int y, unsigned int z);
	inline unsigned int CLFXMD5_G(unsigned int x, unsigned int y, unsigned int z);
	inline unsigned int CLFXMD5_H(unsigned int x, unsigned int y, unsigned int z);
	inline unsigned int CLFXMD5_I(unsigned int x, unsigned int y, unsigned int z);

	inline unsigned int CLFXMD5_ROTATE_LEFT(unsigned int x, unsigned int n);

	inline void CLFXMD5_FF(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int s, unsigned int ac);
	inline void CLFXMD5_GG(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int s, unsigned int ac);
	inline void CLFXMD5_HH(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int s, unsigned int ac);
	inline void CLFXMD5_II(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int s, unsigned int ac);

	inline void CLFXMD5_Encode(unsigned char* output, const unsigned int* input, unsigned int len);
	inline void CLFXMD5_Decode(unsigned int* output, const unsigned char* input, unsigned int len);

	inline void CLFXMD5_DigestToString(std::string& str, const unsigned char digest[16]);

	
	static void CLFXMD5_Transform(unsigned int state[4], const unsigned char block[64]);

	void CLFXMD5_Init(CLFXMD5Context& context);
	void CLFXMD5_Update(CLFXMD5Context& context, const unsigned char* input, unsigned int inputLen);
	void CLFXMD5_Final(unsigned char digest[16], CLFXMD5Context& context);

	void CLFXMakeMD5(std::string& md5, const char* input);

	
	inline unsigned int CLFXMD5_F(unsigned int x, unsigned int y, unsigned int z)
	{
		return ((x & y) | ((~x) & z));
	}

	//-------------------------------------------------------------------------
	inline unsigned int CLFXMD5_G(unsigned int x, unsigned int y, unsigned int z)
	{
		return ((x & z) | (y & (~z)));
	}

	//-------------------------------------------------------------------------
	inline unsigned int CLFXMD5_H(unsigned int x, unsigned int y, unsigned int z)
	{
		return (x ^ y ^ z);
	}

	//-------------------------------------------------------------------------
	inline unsigned int CLFXMD5_I(unsigned int x, unsigned int y, unsigned int z)
	{
		return (y ^ (x | (~z)));
	}

	//-------------------------------------------------------------------------
	inline unsigned int CLFXMD5_ROTATE_LEFT(unsigned int x, unsigned int n)
	{
		return ((x << n) | (x >> (32-n)));
	}

	//-------------------------------------------------------------------------
	inline void CLFXMD5_FF(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int s, unsigned int ac)
	{
		a += CLFXMD5_F(b, c, d) + x + ac;
		a = CLFXMD5_ROTATE_LEFT(a, s);
		a += b;
	}

	//-------------------------------------------------------------------------
	inline void CLFXMD5_GG(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int s, unsigned int ac)
	{
		a += CLFXMD5_G(b, c, d) + x + ac;
		a = CLFXMD5_ROTATE_LEFT(a, s);
		a += b;
	}

	//-------------------------------------------------------------------------
	inline void CLFXMD5_HH(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int s, unsigned int ac)
	{
		a += CLFXMD5_H(b, c, d) + x + ac;
		a = CLFXMD5_ROTATE_LEFT(a, s);
		a += b;
	}

	//-------------------------------------------------------------------------
	inline void CLFXMD5_II(unsigned int& a, unsigned int b, unsigned int c, unsigned int d, unsigned int x, unsigned int s, unsigned int ac)
	{
		a += CLFXMD5_I(b, c, d) + x + ac;
		a = CLFXMD5_ROTATE_LEFT(a, s);
		a += b;
	}

	//-------------------------------------------------------------------------
	inline void CLFXMD5_Encode(unsigned char* output, const unsigned int* input, unsigned int len)
	{
		unsigned int i;
		unsigned int j;

		for(i=0, j=0; j<len; ++i, j += 4)
		{
			output[j]	= static_cast<unsigned char>((input[i]) & 0xff);
			output[j+1]	= static_cast<unsigned char>((input[i] >> 8) & 0xff);
			output[j+2]	= static_cast<unsigned char>((input[i] >> 16) & 0xff);
			output[j+3]	= static_cast<unsigned char>((input[i] >> 24) & 0xff);
		}
	}

	//-------------------------------------------------------------------------
	inline void CLFXMD5_Decode(unsigned int* output, const unsigned char* input, unsigned int len)
	{
		unsigned int i;
		unsigned int j;

		for(i=0, j=0; j<len; ++i, j+=4)
		{
			output[i] =
				(static_cast<unsigned int>(input[j])) |
				(static_cast<unsigned int>(input[j+1]) << 8) |
				(static_cast<unsigned int>(input[j+2]) << 16) |
				(static_cast<unsigned int>(input[j+3]) << 24);
		}
	}

	//-------------------------------------------------------------------------
	inline void CLFXMD5_DigestToString(std::string& str, const unsigned char digest[16])
	{
		char res[33];
		char* p = res;
		unsigned int i;

		for(i=0; i<16; ++i, p+=2)
			::sprintf(p, "%02x", digest[i]);

		str = res;
	}

	//-------------------------------------------------------------------------
	static void CLFXMD5_Transform(unsigned int state[4], const unsigned char block[64])
	{
		unsigned int a = state[0];
		unsigned int b = state[1];
		unsigned int c = state[2];
		unsigned int d = state[3];
		unsigned int x[16];

		::memset(x, 0, 16);//Bug???

		CLFXMD5_Decode(x, block, 64);

		//--Round 1
		CLFXMD5_FF(a, b, c, d, x[ 0], S11, 0xd76aa478);	//-- 1
		CLFXMD5_FF(d, a, b, c, x[ 1], S12, 0xe8c7b756);	//-- 2
		CLFXMD5_FF(c, d, a, b, x[ 2], S13, 0x242070db);	//-- 3
		CLFXMD5_FF(b, c, d, a, x[ 3], S14, 0xc1bdceee);	//-- 4
		CLFXMD5_FF(a, b, c, d, x[ 4], S11, 0xf57c0faf);	//-- 5
		CLFXMD5_FF(d, a, b, c, x[ 5], S12, 0x4787c62a);	//-- 6
		CLFXMD5_FF(c, d, a, b, x[ 6], S13, 0xa8304613);	//-- 7
		CLFXMD5_FF(b, c, d, a, x[ 7], S14, 0xfd469501);	//-- 8
		CLFXMD5_FF(a, b, c, d, x[ 8], S11, 0x698098d8);	//-- 9
		CLFXMD5_FF(d, a, b, c, x[ 9], S12, 0x8b44f7af);	//-- 10
		CLFXMD5_FF(c, d, a, b, x[10], S13, 0xffff5bb1);	//-- 11
		CLFXMD5_FF(b, c, d, a, x[11], S14, 0x895cd7be);	//-- 12
		CLFXMD5_FF(a, b, c, d, x[12], S11, 0x6b901122);	//-- 13
		CLFXMD5_FF(d, a, b, c, x[13], S12, 0xfd987193);	//-- 14
		CLFXMD5_FF(c, d, a, b, x[14], S13, 0xa679438e);	//-- 15
		CLFXMD5_FF(b, c, d, a, x[15], S14, 0x49b40821);	//-- 16

		//--Round 2
		CLFXMD5_GG(a, b, c, d, x[ 1], S21, 0xf61e2562);	//-- 17
		CLFXMD5_GG(d, a, b, c, x[ 6], S22, 0xc040b340);	//-- 18
		CLFXMD5_GG(c, d, a, b, x[11], S23, 0x265e5a51);	//-- 19
		CLFXMD5_GG(b, c, d, a, x[ 0], S24, 0xe9b6c7aa);	//-- 20
		CLFXMD5_GG(a, b, c, d, x[ 5], S21, 0xd62f105d);	//-- 21
		CLFXMD5_GG(d, a, b, c, x[10], S22, 0x02441453);	//-- 22
		CLFXMD5_GG(c, d, a, b, x[15], S23, 0xd8a1e681);	//-- 23
		CLFXMD5_GG(b, c, d, a, x[ 4], S24, 0xe7d3fbc8);	//-- 24
		CLFXMD5_GG(a, b, c, d, x[ 9], S21, 0x21e1cde6);	//-- 25
		CLFXMD5_GG(d, a, b, c, x[14], S22, 0xc33707d6);	//-- 26
		CLFXMD5_GG(c, d, a, b, x[ 3], S23, 0xf4d50d87);	//-- 27
		CLFXMD5_GG(b, c, d, a, x[ 8], S24, 0x455a14ed);	//-- 28
		CLFXMD5_GG(a, b, c, d, x[13], S21, 0xa9e3e905);	//-- 29
		CLFXMD5_GG(d, a, b, c, x[ 2], S22, 0xfcefa3f8);	//-- 30
		CLFXMD5_GG(c, d, a, b, x[ 7], S23, 0x676f02d9);	//-- 31
		CLFXMD5_GG(b, c, d, a, x[12], S24, 0x8d2a4c8a);	//-- 32

		//--Round 3
		CLFXMD5_HH(a, b, c, d, x[ 5], S31, 0xfffa3942);	//-- 33
		CLFXMD5_HH(d, a, b, c, x[ 8], S32, 0x8771f681);	//-- 34
		CLFXMD5_HH(c, d, a, b, x[11], S33, 0x6d9d6122);	//-- 35
		CLFXMD5_HH(b, c, d, a, x[14], S34, 0xfde5380c);	//-- 36
		CLFXMD5_HH(a, b, c, d, x[ 1], S31, 0xa4beea44);	//-- 37
		CLFXMD5_HH(d, a, b, c, x[ 4], S32, 0x4bdecfa9);	//-- 38
		CLFXMD5_HH(c, d, a, b, x[ 7], S33, 0xf6bb4b60);	//-- 39
		CLFXMD5_HH(b, c, d, a, x[10], S34, 0xbebfbc70);	//-- 40
		CLFXMD5_HH(a, b, c, d, x[13], S31, 0x289b7ec6);	//-- 41
		CLFXMD5_HH(d, a, b, c, x[ 0], S32, 0xeaa127fa);	//-- 42
		CLFXMD5_HH(c, d, a, b, x[ 3], S33, 0xd4ef3085);	//-- 43
		CLFXMD5_HH(b, c, d, a, x[ 6], S34, 0x04881d05);	//-- 44
		CLFXMD5_HH(a, b, c, d, x[ 9], S31, 0xd9d4d039);	//-- 45
		CLFXMD5_HH(d, a, b, c, x[12], S32, 0xe6db99e5);	//-- 46
		CLFXMD5_HH(c, d, a, b, x[15], S33, 0x1fa27cf8);	//-- 47
		CLFXMD5_HH(b, c, d, a, x[ 2], S34, 0xc4ac5665);	//-- 48

		//--Round 4
		CLFXMD5_II(a, b, c, d, x[ 0], S41, 0xf4292244);	//-- 49
		CLFXMD5_II(d, a, b, c, x[ 7], S42, 0x432aff97);	//-- 50
		CLFXMD5_II(c, d, a, b, x[14], S43, 0xab9423a7);	//-- 51
		CLFXMD5_II(b, c, d, a, x[ 5], S44, 0xfc93a039);	//-- 52
		CLFXMD5_II(a, b, c, d, x[12], S41, 0x655b59c3);	//-- 53
		CLFXMD5_II(d, a, b, c, x[ 3], S42, 0x8f0ccc92);	//-- 54
		CLFXMD5_II(c, d, a, b, x[10], S43, 0xffeff47d);	//-- 55
		CLFXMD5_II(b, c, d, a, x[ 1], S44, 0x85845dd1);	//-- 56
		CLFXMD5_II(a, b, c, d, x[ 8], S41, 0x6fa87e4f);	//-- 57
		CLFXMD5_II(d, a, b, c, x[15], S42, 0xfe2ce6e0);	//-- 58
		CLFXMD5_II(c, d, a, b, x[ 6], S43, 0xa3014314);	//-- 59
		CLFXMD5_II(b, c, d, a, x[13], S44, 0x4e0811a1);	//-- 60
		CLFXMD5_II(a, b, c, d, x[ 4], S41, 0xf7537e82);	//-- 61
		CLFXMD5_II(d, a, b, c, x[11], S42, 0xbd3af235);	//-- 62
		CLFXMD5_II(c, d, a, b, x[ 2], S43, 0x2ad7d2bb);	//-- 63
		CLFXMD5_II(b, c, d, a, x[ 9], S44, 0xeb86d391);	//-- 64

		state[0] += a;
		state[1] += b;
		state[2] += c;
		state[3] += d;
	}

	//-------------------------------------------------------------------------
	void CLFXMD5_Init(CLFXMD5Context& context)
	{
		context.rgCount[0] = 0;
		context.rgCount[1] = 0;

		context.rgState[0] = 0x67452301;
		context.rgState[1] = 0xefcdab89;
		context.rgState[2] = 0x98badcfe;
		context.rgState[3] = 0x10325476;
	}

	//-------------------------------------------------------------------------
	void CLFXMD5_Update(CLFXMD5Context& context, const unsigned char* input, unsigned int inputLen)
	{
		unsigned int i;
		unsigned int index;
		unsigned int partLen;

		index = (context.rgCount[0] >> 3) & 0x3f;

		context.rgCount[0] += (inputLen << 3);

		if(context.rgCount[0] < (inputLen << 3))
			context.rgCount[1] += 1;

		context.rgCount[1] += (inputLen >> 29);

		partLen = 64 - index;

		if(inputLen >= partLen)
		{
			::memcpy(&(context.rgBuffer[index]), input, partLen);
			CLFXMD5_Transform(context.rgState, context.rgBuffer);

			for(i = partLen; i + 63 < inputLen; i += 64)
				CLFXMD5_Transform(context.rgState, &input[i]);

			index = 0;
		}
		else
			i = 0;

		::memcpy(&(context.rgBuffer[index]), &(input[i]), inputLen-i);
	}

	//-------------------------------------------------------------------------
	void CLFXMD5_Final(unsigned char digest[16], CLFXMD5Context& context)
	{
		unsigned char bits[8];
		unsigned int index;
		unsigned int padLen;

		CLFXMD5_Encode(bits, context.rgCount, 8);

		index = static_cast<unsigned int>((context.rgCount[0] >> 3) & 0x3f);
		padLen = (index < 56) ? (56 - index) : (120 - index);
		CLFXMD5_Update(context, PADDING, padLen);
		CLFXMD5_Update(context, bits, 8);
		CLFXMD5_Encode(digest, context.rgState, 16);

		::memset(&context, 0, sizeof(CLFXMD5Context));
	}

	//-------------------------------------------------------------------------
	void CLFXMakeMD5(std::string& md5, const char* input)
	{
		CLFXMD5Context context;
		unsigned char digest[16];
		unsigned int len = static_cast<unsigned int>(::strlen(input));

		CLFXMD5_Init(context);
		CLFXMD5_Update(context, reinterpret_cast<unsigned char*>(const_cast<char*>(input)), len);
		CLFXMD5_Final(digest, context);

		CLFXMD5_DigestToString(md5, digest);
	}

//==========================================================================================================================
